package com.ysd.testValid;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.ysd.common.Result;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.ServletRequest;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

@Aspect
@Component
public class TestValidAspect {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Around("@annotation(ValidRepeat)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();

        // 从切面织入点处通过反射机制获取织入点处的方法
        // 获取切入点所在的方法
        Method method = signature.getMethod();

        // 解析切面的所有参数，放入一个Map
        Map<String, Object> paramsMap = produceParamsMap(joinPoint, signature);

        ValidRepeat customAnnotation = signature.getMethod().getAnnotation(ValidRepeat.class);

        if (customAnnotation != null) {
            String orgId = dynamicParam(customAnnotation.orgId(), paramsMap);
            String tableName = customAnnotation.tableName();
            String coloumName = customAnnotation.coloumName();
            String coloumCode = dynamicParam(customAnnotation.coloumCode(), paramsMap);

            String id = dynamicParam(customAnnotation.id(), paramsMap);
            String sql = "";
            if (StrUtil.isNotEmpty(id)) {
                sql = "SELECT * FROM " + tableName + " WHERE " + coloumName + "=" + "'" + coloumCode + "'" + " AND DEL_FLAG =0 AND ORG_ID=" + "'" + orgId + "'"
                        + "AND id <> " + "'" + id + "'";
            } else {
                sql = "SELECT * FROM " + tableName + " WHERE " + coloumName + "=" + "'" + coloumCode + "'" + " AND DEL_FLAG =0 AND ORG_ID=" + "'" + orgId + "'";
            }
            List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);

            if (maps.size() > 0) {
                throw new BusinessException("编码重复异常");
            }
            // 处理注解逻辑
        }
        return joinPoint.proceed();
    }

    /**
     * 处理注解中的动态参数，把变量占位符替换成实际的变量值
     */
    private static String dynamicParam(String logContent, Map<String, Object> paramsMap) {
        AtomicReference<String> actual = new AtomicReference<>(logContent);
        if (StringUtils.isBlank(logContent)) {
            return actual.get();
        }
        paramsMap.entrySet().stream().forEach(p -> {
            if (actual.get().contains(p.getKey())) {
                actual.set(actual.get().replaceAll(p.getKey(), p.getValue() + ""));
            }
        });
        return actual.get();
    }

    /**
     * 把切面中的各种不同类型的参数信息，统一放入一个Map，从而不需要再去别的变量里面寻找参数
     */
    private Map<String, Object> produceParamsMap(JoinPoint joinPoint, MethodSignature signature) {
        Map<String, Object> paramsMap = new HashMap<>(); // 包含此切面的所有参数值的Map
        String[] argNames = signature.getParameterNames(); // 参数名
        Object[] argObjects = joinPoint.getArgs(); // 参数
        List<String> argNameList = Arrays.asList(argNames);
        for (int i = 0; i < argNameList.size(); i++) {
            Class<?> clazz = argObjects[i].getClass();
            // 把简单数据类型的参数放入参数Map （包括了Request对象中的ParameterMap）
            Object currentParam = joinPoint.getArgs()[i];
            if (isSimpleType(clazz.getName()) || argObjects[i] instanceof List) {
                paramsMap.put(argNameList.get(i), currentParam);
                continue;
            }

            // 把Request对象中的Attribute属性放入参数Map
            if (currentParam instanceof ServletRequest) {
                ServletRequest request = (ServletRequest) currentParam;
                Enumeration<String> attr = request.getAttributeNames();
                while (attr.hasMoreElements()) {
                    String attrName = attr.nextElement();
                    String attrValue = "";
                    if (request.getAttribute(attrName) != null) {
                        attrValue = request.getAttribute(attrName).toString();
                    }
                    paramsMap.put(attrName, attrValue);
                }
                continue;
            }

            // 把不属于以上两种类型的参数（即Spring MVC/Spring Boot自动接收的Bean类型）放入参数Map
            JSONObject object = JSONUtil.parseObj(currentParam);

            object.keySet().stream().forEach(p -> {
                paramsMap.put(p, object.get(p));
            });
        }
        return paramsMap;
    }

    /**
     * 是不是简单数据类型（简单类型无需使用序列化框架，直接取值即可）
     */
    private static boolean isSimpleType(String name) {
        AtomicBoolean result = new AtomicBoolean(false);
        String[] className = {"String", "Integer", "Long", "int", "float", "double", "char"};
        Arrays.asList(className).forEach(n -> {
            if (name.contains(n)) {
                result.set(name.contains(n));
            }
        });
        return result.get();
    }
}
